﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Component.Mesh;
using Component.Passenger;
using Component.UI;
using Config;
using UnityEngine;

namespace Component.Track
{
    public class StationManager : MonoBehaviour
    {
        
        private Queue<StationNode> invisibleNodes;
        /// <summary>
        /// 站点与gameobject的instanceId的关系
        /// </summary>
        private Dictionary<int, StationNode> stations;

        private Task passengerGenerationTask;
        private Task stationGenerationTask;


        [SerializeField] private float scaleLevel = 1;
        [SerializeField] private int stationCapacity = 20;
        [SerializeField] private int initNum = 2;
        [SerializeField] private bool autoStart;
        private StationNode[] children;
        
        private void Start()
        {
            invisibleNodes = new Queue<StationNode>();
            stations = new Dictionary<int, StationNode>();
            
            children = GetComponentsInChildren<StationNode>();
            
            foreach (var t in children)
            {
                invisibleNodes.Enqueue(t);
                t.gameObject.SetActive(false);
            }

            if (autoStart)
            {
                StationInit();
                stationGenerationTask = new Task(GenerateStation());
                passengerGenerationTask = new Task(GeneratePassenger());
            }
        }

        public DrawMeshTools.MeshType GetStationType(int instanceId)
        {
            try
            {
                return stations[instanceId].Type;
            }
            catch (Exception e)
            {
                return DrawMeshTools.MeshType.Line;
            }
        }

        public LineData GetNextLineData(int currentStationId, int currentLineId, Color color)
        {
            return stations[currentStationId].GetLine(Line.GetLine(currentLineId), color).gameObject.layer !=
                   LayerMask.NameToLayer("Line") ? null : stations[currentStationId].GetLine(Line.GetLine(currentLineId),color).LineData;
        }

        public List<DrawMeshTools.MeshType> GetReachable(int stationId, Action<Transform> callback)
        {
            callback.Invoke(stations[stationId].transform.GetChild(0));
            return stations[stationId].ReachableType;
        }

        /// <summary>
        /// 站点生成以及乘客
        /// </summary>
        /// <returns></returns>
        IEnumerator GeneratePassenger()
        {
            while (true)
            {
                //让站点生成乘客
                int[] instanceIds = stations.Keys.ToArray();
                
                print("passengers is generating." + instanceIds.Length + "passengers");
                
                for (int i = 0; i < instanceIds.Length; i++)
                {
                    if (stations[instanceIds[i]].transform.GetChild(0).childCount >= stationCapacity) continue;
                    
                    yield return new WaitForSeconds(TimeScale.PASSENGER_GENERATE_TIME * scaleLevel);
                    PassengerGeneration.Instance.Generate(stations[instanceIds[i]].transform.GetChild(0), instanceIds[i]);
                }

                yield return null;
            }
        }

        IEnumerator GenerateStation()
        {
            while (true)
            {
                print("station is generating");
                yield return new WaitForSeconds(TimeScale.STATION_GENERATE_TIME * scaleLevel);
                if (invisibleNodes.Count > 0)
                {
                    StationNode node = invisibleNodes.Dequeue();
                    node.gameObject.SetActive(true);
                    stations.Add(node.gameObject.GetInstanceID(), node);
                    if (!TransferTools.CurrentType.Contains(node.Type))
                    {
                        TransferTools.CurrentType.Add(node.Type);
                    }
                }
            }
        }
        
        
        /// <summary>
        /// 初始共火车站数量
        /// </summary>
        private void StationInit()
        {
            for (int i = 0; i < initNum - 1; i++)
            {
                StationNode node = invisibleNodes.Dequeue();
                
                stations.Add(node.gameObject.GetInstanceID(), node);
                if (!TransferTools.CurrentType.Contains(node.Type))
                {
                    TransferTools.CurrentType.Add(node.Type);
                }
                node.gameObject.SetActive(true);
            }
        }
        public void StartGenerate()
        {
            passengerGenerationTask?.Stop();
            stationGenerationTask?.Stop();
            
            StationInit();
            stationGenerationTask = new Task(GenerateStation());
            passengerGenerationTask = new Task(GeneratePassenger());
        }
        public void PauseGenerate()
        {
            print("pausing");
            passengerGenerationTask.Pause();
            stationGenerationTask.Pause();
            TakeTaskPause();
        }
        public void ContinueGenerate()
        {
            passengerGenerationTask.Continue();
            stationGenerationTask.Continue();
            TakeTaskContinue();
        }
        public void StopGenerate()
        {
            TakeTaskStop();
            passengerGenerationTask.Stop();
            stationGenerationTask.Stop();
            invisibleNodes = new Queue<StationNode>();
            stations = new Dictionary<int, StationNode>();
            
            foreach (var t in children)
            {
                PassengerGeneration.Instance.ClearAll(t.gameObject.GetInstanceID());
                invisibleNodes.Enqueue(t);
                t.gameObject.SetActive(false);
            }
        }
        private Queue<PassengerTask> takeTasks = new Queue<PassengerTask>();
        
        private void TakeTaskPause()
        {
            foreach (var pt in takeTasks)
            {
                pt.TakeTask.Pause();
            }
        }

        private void TakeTaskContinue()
        {
            foreach (var pt in takeTasks)
            {
                pt.TakeTask.Continue();
            }
        }

        private void TakeTaskStop()
        {
            foreach (var pt in takeTasks)
            {
                pt.TakeTask.Stop();
            }
        }
        
        
        public PassengerTask Passenger {
            get
            {
                PassengerTask task = new PassengerTask(stations);
                takeTasks.Enqueue(task);
                return task;
            }
        }
        public Dictionary<DrawMeshTools.MeshType, List<int>> GetStationNetwork(int currentStationId, int currentLineId,Color color)
        {
            Dictionary<DrawMeshTools.MeshType, List<int>> result = new Dictionary<DrawMeshTools.MeshType, List<int>>();
            
            DrawLineTools originalLine = Line.GetLine(currentLineId);
            
            StationNode nextStation = originalLine.GetNode(stations[currentStationId]);
            DrawLineTools nextLine = originalLine;
            
            while (nextStation != null)
            {
                nextLine = nextStation.GetLine(nextLine, color);
                if (!result.ContainsKey(nextStation.Type))
                {
                    result.Add(nextStation.Type, new List<int>());
                }
                result[nextStation.Type].Add(nextStation.GetInstanceID());
                if (nextLine == null)
                {
                    break;
                }
                nextStation = nextLine.GetNode(nextStation);
            }
            return result;
        }

        public class PassengerTask
        {
            private Dictionary<DrawMeshTools.MeshType, int> result;
            public Task TakeTask;

            private Dictionary<int, StationNode> stations;

            public PassengerTask(Dictionary<int, StationNode> stations)
            {
                this.stations = stations;
            }
            /// <summary>
            /// 暴露给TrainNode的搭载乘客方法
            /// </summary>
            /// <param name="stationId">站点instanceId</param>
            /// <param name="currentLineId">线路instanceId</param>
            /// <param name="remainCapacity">火车剩余容量</param>
            /// <param name="callback">返回结果集</param>
            public void TakePassenger(int stationId, int currentLineId, int remainCapacity, Action<Dictionary<DrawMeshTools.MeshType, int>> callback)
            {
                Color colorType = Line.GetLineColor(currentLineId);
                
                var station = stations[stationId];

                StationNode nextStation = null;
                try
                {
                    nextStation = station.GetLine(colorType).gameObject.GetInstanceID() == currentLineId ? 
                        station.GetLine(station.GetLine(colorType), colorType).GetNode(station) : station.GetLine(colorType).GetNode(station);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                
                if (nextStation == null)
                {
                    nextStation = Line.GetLine(currentLineId).GetNode(station);
                }
                
                var passengers = PassengerGeneration.Instance.GetPassengers(stationId);
           
                
                result = DrawMeshTools.CreateMeshTypes();
                
                if (TakeTask == null || (TakeTask != null && !TakeTask.Running))
                {
                    TakeTask = new Task(TakePassengerTask(station, nextStation, passengers, remainCapacity, colorType, colorType));
                    TakeTask.Finished += manual =>
                    {
                        callback.Invoke(result);
                    };
                }
            }
            /// <summary>
            /// 重置需要上车的乘客
            /// </summary>
            private IEnumerator TakePassengerTask(StationNode station, StationNode nextStation, List<PassengerNode> passengerNodes, int remainCapacity, Color lineColor, Color originalColor)
            {
                List<PassengerNode> reset = TakePassengerHelper(station, nextStation, passengerNodes, remainCapacity,
                    lineColor, originalColor);
                foreach (var t in reset)
                {
                    PassengerGeneration.ResetPassenger(t);
                    yield return new WaitForSeconds(TimeScale.PASSENGER_ON_TIME);
                }
            }
            /// <summary>
            /// 遍历所有可能抵达或者间接可达路径
            /// </summary>
            /// <param name="station">当前站点</param>
            /// <param name="nextStation">下一站点</param>
            /// <param name="passengerNodes">当前站点乘客</param>
            /// <param name="remainCapacity">火车剩余容量</param>
            /// <param name="lineColor">当前路线颜色</param>
            /// <param name="originalColor">间接路线颜色</param>
            /// <returns>待重置的乘客</returns>
            private List<PassengerNode> TakePassengerHelper(StationNode station, StationNode nextStation, List<PassengerNode> passengerNodes, int remainCapacity, Color lineColor, Color originalColor)
            {
                List<PassengerNode> reset = new List<PassengerNode>();
                if (station.transform.GetChild(0).childCount > 0)
                {
                    var original = station;
                    var currentStation = station;
                    //遍历同路线的站点
                    while (nextStation != null && remainCapacity > reset.Count)
                    {
                        //依据站点遍历所有乘客
                        for (int i = 0; i < passengerNodes.Count && remainCapacity > reset.Count; )
                        {
                            if (passengerNodes[i].Type == nextStation.Type)
                            {
                                //告诉站点可以搭载此乘客，并使在当前路上的，仅做转站用
                                if (!original.ReachableType.Contains(nextStation.Type) && lineColor != originalColor)
                                {
                                    original.ReachableType.Add(nextStation.Type);
                                }

                                //添加到结果集，并除去乘客
                                result[nextStation.Type] = result[nextStation.Type] + 1;
                                reset.Add(passengerNodes[i]);
                                passengerNodes.RemoveAt(i);
                            }
                            else
                            {
                                i++;
                            }
                        }
                        //获取一下个站点，更改当前站点
                        var temp = nextStation;
                        if (currentStation.GetLine(lineColor) == null) //在撤销线上的时候，该线上结点的信息已经被初始化了，避免获得null
                        {
                            nextStation = nextStation.GetNotEndLine(lineColor).GetNode(nextStation);
                        }
                        else
                        {
                            if (nextStation.GetLine(currentStation.GetLine(lineColor), lineColor) != null)
                            {
                                nextStation = nextStation.GetLine(currentStation.GetLine(lineColor), lineColor).GetNode(nextStation);
                            }
                            else
                            {
                                nextStation = null;
                            }
                        }

                        currentStation = temp;
                        
                        StationNode.StationResult[] otherNeighbours = currentStation.GetNeighbours(lineColor, currentStation);
                        
                        foreach (var neighbour in otherNeighbours)
                        {
                            reset.AddRange(TakePassengerHelper(currentStation, neighbour.Station, passengerNodes, remainCapacity - reset.Count,
                                neighbour.Color, lineColor));
                        }
                    }
                }
                return reset;
            }
        }
        
        public struct PassengerResult
        {
            public List<PassengerNode> PassengerNodes;
            public List<DrawMeshTools.MeshType> CurrentLineType;
        }
    }
    
    
    public class StationGeneration
    {
        private static StationManager instance;
        public static StationManager Instance
        {
            get
            {
                if (instance == null)
                {
                    Debug.LogError("stationManager is null, start to find station Manager");
                    // instance = GameObject.Find("Guangzhou").GetComponent<StationManager>();
                }
                return instance;
            }
            set => instance = value;
        }
    }

}